home *** CD-ROM | disk | FTP | other *** search
/ Garbo / Garbo.cdr / mac / hypercrd / xcmd / dxcmds34.sit / Dartmouth XCMD's 3.4.3 / card_17742.txt < prev    next >
Text File  |  1990-04-17  |  16KB  |  460 lines

  1. -- card: 17742 from stack: in.3
  2. -- bmap block id: 0
  3. -- flags: 4000
  4. -- background id: 3241
  5. -- name: ConvertDate
  6. ----- HyperTalk script -----
  7. on Install
  8.   get ChooseTargetStack()
  9.   InstallResource XFCN,ConvertDate,it
  10. end Install
  11.  
  12.  
  13. -- part 1 (field)
  14. -- low flags: 81
  15. -- high flags: 2007
  16. -- rect: left=12 top=26 right=298 bottom=491
  17. -- title width / last selected line: 0
  18. -- icon id / first selected line: 0 / 0
  19. -- text alignment: 0
  20. -- font id: 22
  21. -- text size: 10
  22. -- style flags: 0
  23. -- line height: 13
  24. -- part name: Source
  25.  
  26.  
  27. -- part 2 (button)
  28. -- low flags: 00
  29. -- high flags: A003
  30. -- rect: left=299 top=300 right=322 bottom=438
  31. -- title width / last selected line: 0
  32. -- icon id / first selected line: 0 / 0
  33. -- text alignment: 1
  34. -- font id: 0
  35. -- text size: 12
  36. -- style flags: 0
  37. -- line height: 16
  38. -- part name: Show Pascal Source
  39. ----- HyperTalk script -----
  40. on mouseUp
  41.   set the visible of card field 1 to not the visible of card field 1
  42.   if the visible of card field 1 is true then
  43.     set the name of me to "Hide Pascal Source"
  44.   else set the name of me to "Show Pascal Source"
  45. end mouseUp
  46.  
  47.  
  48.  
  49. -- part 3 (button)
  50. -- low flags: 00
  51. -- high flags: A003
  52. -- rect: left=80 top=300 right=322 bottom=180
  53. -- title width / last selected line: 0
  54. -- icon id / first selected line: 0 / 0
  55. -- text alignment: 1
  56. -- font id: 0
  57. -- text size: 12
  58. -- style flags: 0
  59. -- line height: 16
  60. -- part name: Try It
  61. ----- HyperTalk script -----
  62. on mouseUp
  63.   ask "What is your birthday?"
  64.   if it is not empty then
  65.     put convertdate(it) into birthday
  66.     if word 1 of birthday is not "Error" then
  67.       get the date
  68.       put convertdate(it) into today
  69.       subtract birthday from today
  70.       divide today by 60
  71.       divide today by 60
  72.       divide today by 24
  73.       answer "Today is your" && today+1 & "th day.  Congratulations!"
  74.     else
  75.       answer "That doesn't make sense as a date."
  76.     end if
  77.   end if
  78. end mouseUp
  79.  
  80.  
  81.  
  82. -- part contents for background part 16
  83. ----- text -----
  84. CONVERTDATE XFCN version 1.0.1
  85. Kevin Calhoun
  86.  
  87. ConvertDate performs a function identical to that of the HyperTalk "convert" command--it converts a date expressed in one of the standard HyperCard formats into another expression, in a specified format, for the same date.  (The standard formats for dates are defined on page 99 of the HyperCard Script Language Guide.)
  88.  
  89. The advantage of using ConvertDate rather than the HyperTalk command "convert" is that ConvertDate can recognize dates far beyond the range of those that HyperCard is currently capable of handling properly:  from January 1, 1904 to February 6, 2040.  ConvertDate relies on routines in the new version of the Script Manager that handle all dates within about 35 thousand years of January 1, 1904; however, there are other features of these Script Manager routines that limit the range more severely.  When converting dates in the dateItems format or the seconds format, ConvertDate will properly convert all dates falling between January 1 of the year 1 and December 31, 9999.  When converting dates in the long date, short date, or abbreviated date formats, ConvertDate works properly for dates from January 1, 1000 to December 31, 9999*.
  90.  
  91. ConvertDate recognizes the format of the input expression by itself, whether the format is seconds, dateItems, long date, short date, or abbreviated date.
  92.  
  93. WHAT IT'S GOOD FOR
  94. ConvertDate is intended chiefly for those users who need to sort the cards of a stack by date.  Use ConvertDate to convert the dates to seconds format; then use the HyperTalk command "sort" to sort the stack.
  95.  
  96. ConvertDate is also useful for determining which day of the week it was when you were born, how many days have passed since July 4, 1776, and all that sort of thing.
  97.  
  98. INVOKING CONVERTDATE
  99.  
  100. get ConvertDate(inputExpression,<outputFormat>)
  101.  
  102. result:  a date in the format specified
  103.  
  104. The second parameter can be any of the standard date formats as defined on page 99 of the HyperCard Script Language Guide.  If there is no second parameter, or if it is something other than one of the standard formats, ConvertDate defaults to seconds.
  105.  
  106. EXAMPLES
  107.  
  108. get ConvertDate(card field 2,"abbr date")
  109. get ConvertDate("Wednesday, March 15, 1989","dateItems")
  110. get ConvertDate("1959,5,14,0,0,0,0","long date")  -- note that when using dateItems 
  111.                   format, it isn't necessary to enter the correct day of week
  112.  
  113. *  It is not yet clear to me whether the Script Manager routines that ConvertDate relies on take into account, in localized versions of the System Software, any or all of the historical twiddlings of the official calendar that apply or have applied to a given geographical region.  In other words, if you're attempting to calculate the interest owed on a longstanding loan made earlier than 1914 by your ancestors to the ancestors of a debtor, I recommend hiring a specialist.
  114.  
  115. Here is the reason that January 1, 1000 is the earliest date that can be converted successfully when expressed in long date, short date, or abbreviated date format:  the Script Manager routine String2Date adds 1900 to years earlier than 100, so that 1/1/75 means January 1, 1975, and 1000 to years earlier than 1000, so that 1/1/975 means the same as 1/1/75.  When using ConvertDate with an input date in dateItems format or seconds format, the XFCN doesn't call String2Date to interpret the input; therefore if you want to handle dates between January 1, 1, and January 1, 1000, you can ensure correctness by representing dates in dateItems format or seconds format and converting them to your preferred format for display purposes.  The range of the output year, 1 to 9999, is imposed by the Script Manager routine LongSecs2Date.
  116.  
  117. REVISION HISTORY
  118. March 17, 1989 -- 1.0 release.
  119. March 31, 1989 -- 1.0.1.  Fixed problem with dates input in short date format.  Input expression, if a container, should no long be quoted.
  120.  
  121.  
  122. -- part contents for card part 1
  123. ----- text -----
  124. UNIT ConvertDateUnit;
  125.  
  126. { ConvertDate XFCN ┬⌐1989 by the Trustees of Dartmouth College }
  127. { Written by Kevin Calhoun }
  128.  
  129. { This source compatible with MPW Pascal 3.0 }
  130.  
  131. (*
  132. pascal ConvertDate.p
  133. Link -m ENTRYPOINT Γêé
  134.      -o "YourFile" Γêé
  135.      -rt XFCN=1904 Γêé
  136.      -sg ConvertDate Γêé
  137.      ConvertDate.p.o Γêé
  138.     "{Libraries}"interface.o Γêé
  139.     "{PLibraries}"Paslib.o Γêé
  140.     "{PLibraries}"SANElib.o Γêé
  141.     "{Libraries}"HyperXLib.o
  142. *)
  143.  
  144. {$R-}
  145.  
  146. INTERFACE
  147.   USES
  148.     Types,
  149.     Memory,
  150.     Script,
  151.     SANE,
  152.     HyperXCmd;
  153.  
  154.   PROCEDURE EntryPoint (paramPtr : XCMDPtr);
  155.  
  156. IMPLEMENTATION
  157.  
  158. TYPE
  159.   HCDateForm = (seconds, dateItems, lngDate, shrtDate,
  160.     abbrvDate, lngTime, shrtTime, abbrvTime);
  161.  
  162. PROCEDURE ConvertDate(paramPtr: XCmdPtr); FORWARD;
  163.  
  164. PROCEDURE Entrypoint(paramPtr: XCmdPtr);
  165. BEGIN
  166.   ConvertDate(paramPtr);
  167. END;
  168.  
  169. FUNCTION ScriptManagerInstalled: BOOLEAN;
  170.   const
  171.     UnimplCoreRoutine = $9F;
  172.     ScriptUtil = $B5;
  173. BEGIN
  174.   ScriptManagerInstalled :=
  175.     GetTrapAddress(UnimplCoreRoutine) <> GetTrapAddress(ScriptUtil);
  176. END;
  177.  
  178. PROCEDURE PassReturnValue(paramPtr: XCMDPtr; s: Str255);
  179. BEGIN
  180.   paramPtr^.returnValue := PasToZero(paramPtr, s);
  181. END;
  182.  
  183. FUNCTION CommaCount(str: Str255): INTEGER;
  184. { How many commas are in a string? }
  185.   VAR
  186.     count,i: INTEGER;
  187. BEGIN
  188.   count := 0;
  189.   FOR i := 1 to LENGTH(str) DO
  190.     IF str[i] = ',' THEN count := count+1;
  191.   CommaCount := count;
  192. END;
  193.  
  194. FUNCTION IsDateItems(paramPtr: XCMDPtr; hndl: Handle): BOOLEAN;
  195. { Determine whether h contains a comma-separated list of 7 items. }
  196.   VAR
  197.     str: Str255;
  198. BEGIN
  199.   ZeroToPas(paramPtr, hndl^, str);
  200.   IsDateItems := (CommaCount(str)=6);
  201. END;
  202.  
  203. PROCEDURE GetNextItem(sPtr: StringPtr; var index: INTEGER;
  204.                      var item: Str255);
  205. { Scan a string from position index until the next comma, or until
  206.   the end of the string, and return the characters we collected
  207.   in item. }
  208.   VAR
  209.     start: INTEGER;
  210.     sLength: INTEGER;
  211. BEGIN
  212.   sLength := LENGTH(sPtr^);
  213.   IF index <= sLength THEN
  214.     BEGIN
  215.     start := index;
  216.     WHILE (sPtr^[index] <> ',') AND (index < sLength) DO index := index+1;
  217.     IF sPtr^[index] = ',' THEN index:=index-1;
  218.     item := COPY(sPtr^, start, (index-start+1));
  219.     index := index+2;
  220.     END;
  221. END;
  222.  
  223. PROCEDURE Handle2LongDate(paramPtr: XCMDPtr; h: Handle; VAR dateTime: LongDateRec);
  224. { convert a comma-separated list in h into a LongDateRec }
  225.   VAR
  226.     str: Str255;
  227.     i,index: INTEGER;
  228.     stringPtr: Ptr;
  229.     numStr: Str255;
  230.     return: Handle;
  231.     num: INTEGER;
  232. BEGIN
  233.   ZeroToPas(paramPtr, h^, str);
  234.   index := 1;
  235.   dateTime.list[0] := 0;
  236.   for i := 1 to 7 do
  237.     begin
  238.     GetNextItem(@str,index,numStr);
  239.     num := StrToNum(paramPtr, numStr);
  240.     dateTime.list[i] := num;
  241.     end;
  242.   for i := 8 to 13 do dateTime.list[i] := 0;
  243. END;
  244.  
  245. FUNCTION IsANumber(paramPtr: XCMDPtr; h: Handle): BOOLEAN;
  246. { call SANE to determine if h contains a valid number }
  247.   VAR
  248.     s: Str255;
  249.     index: INTEGER;
  250.     d: decimal;
  251.     validPrefix: BOOLEAN;
  252.     c: Comp;
  253.     n: NumClass;
  254. BEGIN
  255.   IsANumber := FALSE;
  256.   ZeroToPas(paramPtr, h^, s);
  257.   index := 1;
  258.   Str2Dec(DecStr(s), index, d, validPrefix);
  259.   IF validPrefix AND (index = LENGTH(s) + 1) THEN
  260.     BEGIN
  261.     c := Dec2Num(d);
  262.     n := ClassComp(c);
  263.     CASE n OF
  264.       ZeroNum,NormalNum: IsANumber := TRUE;
  265.       END;
  266.     END;
  267. END;
  268.  
  269. PROCEDURE CompToString(c: Comp; var s: Str255);
  270. { call SANE to convert our LongDateTime to a string }
  271.   VAR
  272.     f: decform;
  273. BEGIN
  274.   with f do
  275.     begin
  276.     style := FixedDecimal;
  277.     digits := 0;
  278.     end;
  279.   Num2Str(f, c, DecStr(s));
  280. END;
  281.  
  282. PROCEDURE Handle2Comp(paramPtr: XCMDPtr; h: Handle; var c: Comp);
  283. { call HyperCard to turn h into a string, and then
  284.   call SANE to turn the string into a Comp }
  285.   VAR
  286.     s: DecStr;
  287. BEGIN
  288.   ZeroToPas(paramPtr, h^, Str255(s));
  289.   c := Str2Num(s);
  290. END;
  291.  
  292. PROCEDURE DateItems2Str(paramPtr: XCMDPtr; lDate: LongDateRec; var s: Str255);
  293. { convert our LongDateRec to a HyperCard comma-separated list }
  294.   VAR
  295.     j: INTEGER;
  296.     num: Str255;
  297. BEGIN
  298.   s := '';
  299.   for j := 1 to 7 do
  300.     begin
  301.     NumToStr(paramPtr, lDate.list[j], num);
  302.     s := CONCAT(s, num, ',');
  303.     end;
  304.   DELETE(s, LENGTH(s), 1);
  305. END;
  306.  
  307. FUNCTION GetHCDateForm(paramPtr: XCMDPtr): HCDateForm;
  308. { grab parameter 2, which determines the format into which we convert
  309.   parameter 1 }
  310. VAR
  311.   s: Str255;
  312. BEGIN
  313.   IF paramPtr^.paramCount > 1 THEN
  314.     BEGIN
  315.     ZeroToPas(paramPtr, paramPtr^.params[2]^, s);
  316.     IF EqualString(s, 'seconds', FALSE, TRUE) THEN
  317.       GetHCDateForm := seconds
  318.     ELSE IF EqualString(s, 'dateItems', FALSE, TRUE) THEN
  319.       GetHCDateForm := dateItems
  320.     ELSE IF EqualString(s, 'long date', FALSE, TRUE) THEN
  321.       GetHCDateForm := lngDate
  322.     ELSE IF EqualString(s, 'short date', FALSE, TRUE) THEN
  323.       GetHCDateForm := shrtDate
  324.     ELSE IF EqualString(s, 'abbreviated date', FALSE, TRUE) OR
  325.             EqualString(s, 'abbrev date', FALSE, TRUE) OR
  326.             EqualString(s, 'abbr date', FALSE, TRUE) THEN
  327.       GetHCDateForm := abbrvDate
  328.     ELSE GetHCDateForm := seconds;
  329.     END
  330.   ELSE GetHCDateForm := seconds;
  331. END;
  332.  
  333. PROCEDURE ZeroDateTime(var dateTime: LongDateRec);
  334. { put zeros everywhere in the LongDateRec --
  335.   the ScriptManager documentation says to be careful, so... }
  336.   VAR j: INTEGER;
  337. BEGIN
  338.   for j := 0 to 13 do dateTime.list[j] := 0;
  339. END;
  340.  
  341. PROCEDURE ZeroTime(var dateTime: LongDateRec);
  342. { ...we'll zero irrelevant stuff again after the
  343.   LongDateRec is returned from String2Date }
  344.   VAR j: INTEGER;
  345. BEGIN
  346.   dateTime.era := 0;
  347.   for j := 4 to 6 do dateTime.list[j] := 0;
  348.   for j := 8 to 13 do dateTime.list[j] := 0;
  349. END;
  350.  
  351. PROCEDURE ConvertDate(paramPtr: XCmdPtr);
  352. TYPE
  353.   DateCacheHandle = ^DateCachePtr;
  354. VAR
  355.   smVers: LONGINT;
  356.   s: Str255;
  357.   h: Handle;
  358.   myCacheHandle: DateCacheHandle;
  359.   hLength, lengthUsed: LONGINT;
  360.   dateTime: LongDateRec;
  361.   lSecs: LongDateTime;
  362.   form: HCDateForm;
  363.   dateStr: Str255;
  364.   err: OSErr;
  365.   
  366. BEGIN
  367.   err := noErr;
  368.   { we need input }
  369.   IF paramPtr^.paramCount > 0 then
  370.     BEGIN
  371.     { check whether the Script Manager is available }
  372.     IF ScriptManagerInstalled THEN
  373.       BEGIN
  374.       { now we check whether Script Manager 2.0 or greater is around }
  375.       smVers := GetEnvirons(smVersion);
  376.       if smVers >= $0200 THEN
  377.         BEGIN
  378.         { we get the input expression }
  379.         h := paramPtr^.params[1];
  380.         err := 0;
  381.         MoveHHi(h);
  382.         HLock(h);
  383.         { we create a DateCacheRecord in the heap and initialize it }
  384.         myCacheHandle := DateCacheHandle(NewHandleClear(SIZEOF(DateCacheRecord)));
  385.         err := MemError;
  386.         if err = noErr then
  387.           begin
  388.           MoveHHi(Handle(myCacheHandle));
  389.           HLock(Handle(myCacheHandle));
  390.           err := InitDateCache(myCacheHandle^);
  391.           if err = noErr then
  392.             begin
  393.             ZeroDateTime(dateTime);
  394.             IF IsDateItems(paramPtr, h) THEN
  395.               BEGIN
  396.             { input was in dateItems format;  we'll parse it ourselves }
  397.               Handle2LongDate(paramPtr, h, dateTime);
  398.             { we'll pull the old in-and-out to make sure 
  399.               we have the right dayOfWeek in our LongDateRec }
  400.               LongDate2Secs(dateTime, lSecs);
  401.               LongSecs2Date(lSecs, dateTime);
  402.               END
  403.             ELSE IF IsANumber(paramPtr, h) THEN
  404.               BEGIN
  405.               Handle2Comp(paramPtr, h, lSecs);
  406.               LongSecs2Date(lSecs, dateTime);
  407.               END
  408.             ELSE
  409.               BEGIN
  410.             { we'll let String2Date parse the input }
  411.               lengthUsed := 0;
  412.               hLength := StringLength(paramPtr, h^);
  413.             { call the Script Manager to convert our
  414.               input expression to a LongDateRec }
  415.               err := OSErr(String2Date(h^, hLength,
  416.                   myCacheHandle^, lengthUsed, dateTime));
  417.             { we'll pull the old in-and-out to make sure 
  418.               we have the right dayOfWeek in our LongDateRec }
  419.               IF err in [0..64] then
  420.                 BEGIN
  421.                 ZeroTime(dateTime);
  422.                 LongDate2Secs(dateTime, lSecs);
  423.                 LongSecs2Date(lSecs, dateTime);
  424.                 END;
  425.               END;
  426.             if (err in [0..64]) then
  427.               begin
  428.               err := noErr;
  429.               { get second param, which tells us what
  430.                 the output format should be... }
  431.               form := GetHCDateForm(paramPtr);
  432.               { ...and convert according to that form }
  433.               CASE form OF
  434.                 seconds: CompToString(lSecs, dateStr);
  435.                 dateItems: DateItems2Str(paramPtr, dateTime, dateStr);
  436.                 lngDate: IULDateString(lSecs, longDate, dateStr, nil);
  437.                 shrtDate: IULDateString(lSecs, shortDate, dateStr, nil);
  438.                 abbrvDate: IULDateString(lSecs, abbrevDate, dateStr, nil);
  439.                 end;
  440.               PassReturnValue(paramPtr, dateStr);
  441.               end;  { input was parsed OK or considered a comp }
  442.             end;  { date cache initialized OK }
  443.           DisposHandle(Handle(myCacheHandle));
  444.           end;  { myCacheHandle initialized OK }
  445.         HUnlock(h);
  446.         IF err <> noErr then
  447.           begin
  448.           NumToStr(paramPtr, err, s);
  449.           PassReturnValue(paramPtr, CONCAT('Error ', s));
  450.           end;
  451.         END  { script manager 2.0 or later present }
  452.       else PassReturnValue
  453.         (paramPtr, 'Error -- ConvertDate requires Script Manager 2.0 or greater.');
  454.       END  { script manager installed }
  455.     else PassReturnValue(paramPtr, 'Error -- Script Manager not installed.');
  456.     END  { we had at least 1 parameter }
  457.   else PassReturnValue(paramPtr, 'ConvertDate XFCN 1.0.1, 31 March 1989, ┬⌐1989 Dartmouth College');
  458. END;
  459.  
  460. END.